package com.bit.cfdi.web.dao.impl;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.bit.cfdi.web.dao.ClienteDAO;
import com.bit.cfdi.web.domain.Cliente;
import com.bit.cfdi.web.domain.Comprobante;
import com.bit.cfdi.web.domain.DBConstants;
import com.bit.cfdi.web.domain.Negocio;

@Repository
public class ClienteDAOImpl extends HibernateDaoSupport implements ClienteDAO {

	@Autowired
	public void init(final SessionFactory sessionFactory) {
		setSessionFactory(sessionFactory);
	}
	
	@Override
	@Transactional public void save(Cliente cliente)  throws DataAccessException {
		getHibernateTemplate().saveOrUpdate(cliente);
	}

	@SuppressWarnings("unchecked")
	@Override
	@Transactional public List<Cliente> getAll() throws DataAccessException {
		List<Cliente> clientes = getHibernateTemplate().find(
				" from Cliente as cliente");
		return clientes;
	}

	@SuppressWarnings("unchecked")
	@Override
	@Transactional public Cliente getClienteByNameAndRFC(final String name, final String rfc, 
			final String nombreEmpresa) throws DataAccessException {
		List<Cliente> clientes = getHibernateTemplate().find(
				" from Cliente as cliente" +
				" where cliente.persona.nombre=?" +
				" and cliente.datosFiscales.rfc=? and cliente.negocio.nombre = ?",
				name, rfc, nombreEmpresa);
		return clientes.size()>0?clientes.get(0):null;
	}

	@SuppressWarnings("unchecked")
	@Override
	@Transactional public List<Cliente> getAllfromNegocio(Negocio negocio) throws DataAccessException {
		List<Cliente> clientes = getHibernateTemplate().find(
				" from Cliente as c where c.negocio=?" +
				" and c.status=?", negocio, DBConstants.STATUS_ACTIVE);
		return clientes;
	}

	@SuppressWarnings("unchecked")
	@Override
	@Transactional public List<Long> getMostPurchasedItemsInTime(final Cliente cliente, 
			final Calendar initDate, 
			final Calendar finalDate) throws DataAccessException {		
		List<Long> ret = new ArrayList<Long>();		
		getHibernateTemplate().setMaxResults(10);
		List<Object[]> results = getHibernateTemplate().find(
				"select " +
			    "cc.concepto.idConcepto, sum(cc.cantidad) as totalCantidad, " +
			    "sum(cc.importe) as totalImporte " +
			    "from ComprobanteConcepto as cc " + 
			    "where " + 
			        "cc.comprobante.cliente=? and " +
			        "(cc.comprobante.tipoComprobante = ? or " +
			        "cc.comprobante.tipoComprobante = ?) and " +
			    	"(( cc.comprobante.fechaTimbrado>? " +
			    	"AND cc.comprobante.fechaTimbrado<? ) "+
			    	"OR (cc.comprobante.fechaTimbrado is null " +
			    	"AND cc.comprobante.fechaCreacion>? " +
			    	"AND cc.comprobante.fechaCreacion<? )) " +
			    	"and cc.comprobante.estatus=? " +
			    	"and ((cc.comprobante.parcialidades = cc.comprobante.numeroParcialidad) " +
			    	"or (cc.comprobante.numeroParcialidad=0 and cc.comprobante.parcialidades=null))" +
			    "group by cc.concepto.idConcepto " +
			    "order by sum(cc.cantidad) desc, sum(cc.importe) desc",
			    cliente,
			    Comprobante.TIPO_COMP_INGRESO,
			    Comprobante.TIPO_COMP_TRASLADO,
			    initDate, finalDate,initDate, finalDate, 
			    Comprobante.ESTADO_FACTURA_ACTIVA);
		getHibernateTemplate().setMaxResults(0);
		if(results.size()>0) {
			for(Object[] data : results) {
				ret.add((Long)data[0]);
			}
		}
		return ret;
	}

	@SuppressWarnings("unchecked")
	@Override
	@Transactional public Map<Long, Double> getTotalSalesInTime(Cliente cliente, Calendar initDate,
			Calendar finalDate) {
		Map<Long, Double> ret = new HashMap<Long, Double>();
		List<Object[]> results = getHibernateTemplate().find(
				"select " +
				    "count(c), sum(c.total*c.tipoDeCambio) " +   
				"from " + 
				    "Comprobante as c " +
				"where " + 
				    "c.cliente=? and " +
				    "(c.tipoComprobante = ? or " +
				    "c.tipoComprobante = ?) and" +
				    "((c.fechaTimbrado>?   AND c.fechaTimbrado<? ) " +
			    	"OR(c.fechaTimbrado is null AND c.fechaCreacion>? " +
			    	"AND c.fechaCreacion<? )) "+
			    	"and c.estatus=? " +
			    	"and ((c.parcialidades = c.numeroParcialidad) " +
			    	"or (c.numeroParcialidad=0 and c.parcialidades=null)) " +
				"group by " +
				    "c.cliente.idCliente",
				    cliente, 
				    Comprobante.TIPO_COMP_INGRESO, 
				    Comprobante.TIPO_COMP_TRASLADO, 
				    initDate, finalDate,initDate, finalDate,
				    Comprobante.ESTADO_FACTURA_ACTIVA);
		if(results.size()>0) {
			try {
				if(results.get(0)[1]!=null) {
					ret.put((Long) results.get(0)[0], 
							(Double)results.get(0)[1]);
				}
				else{
					ret.put(0L, 0.0);
				}
			}catch (IndexOutOfBoundsException iob) {
				ret.put(0L, 0.0);
			}
		}
		else {
			ret.put(0L, 0.0);
		}
		return ret;
	}

	@SuppressWarnings("unchecked")
	@Override
	@Transactional public Map<Long, Double> getTotalSalesInTimeByCategory(Cliente cliente,
			String category, Calendar initDate, Calendar finalDate) throws DataAccessException {
		Map<Long, Double> ret = new HashMap<Long, Double>();
		List<Object[]> results = getHibernateTemplate().find(
				"select " +
				    "count(c), sum(c.total*c.tipoDeCambio) " +   
				"from " + 
				    "Comprobante as c " +
				"where " + 
				    "c.cliente=? and " +
				    "c.categoriaComprobante=? and "+
				    "(c.tipoComprobante = ? or " +
				    "c.tipoComprobante = ?) and " +
				    "(( c.fechaTimbrado>? AND c.fechaTimbrado<? ) OR "+
			    	"(c.fechaTimbrado is null AND c.fechaCreacion>? " +
			    	"AND c.fechaCreacion<? )) and "+
			    	"c.estatus=? " +
			    	"and ((c.parcialidades = c.numeroParcialidad) " +
			    	"or (c.numeroParcialidad=0 and c.parcialidades=null)) " +
			    	"group by " +
				    "c.cliente.idCliente",
				    cliente,
				    Comprobante.TIPO_COMP_INGRESO,
				    Comprobante.TIPO_COMP_TRASLADO,
				    category, initDate, finalDate,initDate, finalDate,
				    Comprobante.ESTADO_FACTURA_ACTIVA);
		if(results.size()>0) {
			try {
				Double toStore = new Double("0.0");
				if(results.get(0)[1] instanceof Long) {
					toStore = new Double(
							((Long)(results.get(0)[1])).doubleValue());
				}
				else {
					toStore = (Double) results.get(0)[1];
				}
				ret.put((Long) results.get(0)[0], toStore);
			}catch (IndexOutOfBoundsException iob) {
				ret.put(0L, 0.0);
			}
		}
		else {
			ret.put(0L, 0.0);
		}
		return ret;
	}

	@SuppressWarnings("unchecked")
	@Override
	@Transactional public List<Cliente> searchCustumersByAllFields(final Negocio neg, 
			final String toSearch) throws DataAccessException {
		
		String toSearchLikeForm = "%"+toSearch+"%";
		
		List<Cliente> clientes = getHibernateTemplate().find(
				" from Cliente as c where " +
				"c.negocio=? " +
				"and c.status=? " +
				"and (LOWER(c.persona.nombre) like ? " +
				"or LOWER(c.persona.apellidoPaterno) like ? " +
				"or LOWER(c.persona.apellidoMaterno) like ? " +
				"or LOWER(c.datosFiscales.rfc) like ? " +
				"or LOWER(c.datosFiscales.razon) like ?) ",
				neg,
				DBConstants.STATUS_ACTIVE,
				toSearchLikeForm.toLowerCase(),
				toSearchLikeForm.toLowerCase(),
				toSearchLikeForm.toLowerCase(),
				toSearchLikeForm.toLowerCase(),
				toSearchLikeForm.toLowerCase());
		return clientes;
	}

	@SuppressWarnings("unchecked")
	@Override
	@Transactional public List<Cliente> searchByRfc(String rfc) {
		List<Cliente> clientes = getHibernateTemplate().find(
				"from Cliente wherer rfc = ? ", rfc);
		return clientes;
	}

	@SuppressWarnings("unchecked")
	@Override
	@Transactional public ArrayList<Object> getClientesByRFCLike(String rfc, Negocio negocio) throws DataAccessException {
		String toSearchLikeForm = rfc + "%";		
		List<String> results = getHibernateTemplate().find(
				"select cliente.datosFiscales.rfc from Cliente as cliente " +
				"where cliente.datosFiscales.rfc like ? " +
				"and cliente.status=? " +
				"and cliente.negocio=?", toSearchLikeForm, 
				DBConstants.STATUS_ACTIVE, negocio);		
		ArrayList<Object> listaRfcs = new ArrayList<Object>();		
		if(results.size()>0) {
			for (int i=0; i<results.size(); i++)
				listaRfcs.add(results.get(i));
		} else {
			listaRfcs = null;
		}
		return listaRfcs;
	}

	@SuppressWarnings("unchecked")
	@Override
	@Transactional public Cliente getClienteByRFC(String rfc) throws DataAccessException {
		List<Cliente> clientes = getHibernateTemplate().find(
				" from Cliente as cliente" +				
				" where cliente.datosFiscales.rfc=?",
				rfc);
		return clientes.size()>0?clientes.get(0):null;
	}

	@SuppressWarnings("unchecked")
	@Override
	@Transactional public Cliente getClienteByName(String name) throws DataAccessException {
		List<Cliente> clientes = getHibernateTemplate().find(
				" from Cliente as cliente" +
				" where cliente.datosFiscales.razon=?",
				name);
		return clientes.size()>0?clientes.get(0):null;
	}

	@SuppressWarnings("unchecked")
	@Override
	@Transactional public ArrayList<Object> getClientesByNameLike(String name, Negocio negocio) throws DataAccessException {
		String toSearchLikeForm = name + "%";
		List<String> results = getHibernateTemplate().find(
				"select cliente.datosFiscales.razon from Cliente as cliente " +
				"where UPPER(cliente.datosFiscales.razon) like ? " +
				"and cliente.status=? " +
				"and cliente.negocio=?", toSearchLikeForm.toUpperCase(), 
				DBConstants.STATUS_ACTIVE, negocio);
		
		ArrayList<Object> listaNames= new ArrayList<Object>();
		
		if(results.size()>0)
			for (int i=0; i<results.size(); i++)
				listaNames.add(results.get(i));
		else 
			listaNames = null;
		
		return listaNames;

	}

	@SuppressWarnings("unchecked")
	@Override
	@Transactional public Cliente getClienteByNegocioAndRFC(final Long idNegocio, final String rfc) throws DataAccessException {
		List<Cliente> clientes = getHibernateTemplate().find(
				" from Cliente as cliente" +
				" where cliente.negocio.idNegocio=?" +
				" and cliente.datosFiscales.rfc=?",
				idNegocio, rfc);
		return clientes.size()>0?clientes.get(0):null;
	}

	@SuppressWarnings("unchecked")
	@Override
	@Transactional public List<Cliente> searchClienteRFCRazon(final Negocio neg, 
			final String rfc, final String razon) throws DataAccessException {
		List<Cliente> clientes = getHibernateTemplate().find(
				" FROM Cliente as cliente" +
				" WHERE cliente.negocio=?" +
				" AND cliente.datosFiscales.rfc=?" +
				" AND cliente.datosFiscales.razon=? " +
				" AND cliente.status=?",
				neg,rfc,razon,DBConstants.STATUS_ACTIVE);
		return clientes;
	}

	@SuppressWarnings("unchecked")
	@Override
	@Transactional public Cliente getClienteByRfcNegocio(String rfc, Negocio negocio) throws DataAccessException {
		List<Cliente> clientes = getHibernateTemplate().find(
				" from Cliente as cliente" +				
				" where cliente.datosFiscales.rfc=?" +
				" and cliente.negocio=? " +
				" AND cliente.status=?",
				rfc, negocio, DBConstants.STATUS_ACTIVE);
		return clientes.size()>0?clientes.get(0):null;
	}

	@SuppressWarnings("unchecked")
	@Override
	@Transactional public Cliente getLastInsertedCliente(final Negocio negocio) throws DataAccessException {
		getHibernateTemplate().setMaxResults(1);
		List<Cliente> clientes = getHibernateTemplate().find(
				" from Cliente as cliente" +
				" where cliente.negocio=?", negocio);
		getHibernateTemplate().setMaxResults(0);
		return clientes.size()>0?clientes.get(0):null;
	}
}
